package com.github.gexiao.middleware.hystrix.service.impl;

import com.github.gexiao.middleware.hystrix.annotation.DoHystrix;
import com.github.gexiao.middleware.hystrix.service.FallbackHandler;
import com.github.gexiao.middleware.hystrix.service.HystrixHandler;
import com.netflix.hystrix.*;
import org.aspectj.lang.ProceedingJoinPoint;

import java.lang.reflect.Method;

/**
 * 博客：https://bugstack.cn - 沉淀、分享、成长，让自己和他人都能有所收获！
 * 公众号：bugstack虫洞栈
 * Create by 小傅哥(fustack)
 */
public class DefaultHystrixHandler extends HystrixCommand<Object> implements HystrixHandler {

    private ProceedingJoinPoint jp;
    private Method method;
    private DoHystrix doHystrix;

    public DefaultHystrixHandler() {

        /*********************************************************************************************
         * 置HystrixCommand的属性
         * GroupKey：            该命令属于哪一个组，可以帮助我们更好的组织命令。
         * CommandKey：          该命令的名称
         * ThreadPoolKey：       该命令所属线程池的名称，同样配置的命令会共享同一线程池，若不配置，会默认使用GroupKey作为线程池名称。
         * CommandProperties：   该命令的一些设置，包括断路器的配置，隔离策略，降级设置，以及一些监控指标等。
         * ThreadPoolProperties：关于线程池的配置，包括线程池大小，排队队列的大小等
         *********************************************************************************************/

        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GovernGroup"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("GovernKey"))
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GovernThreadPool"))
                    .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                                                          .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
                    .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10))
        );
    }

    @Override
    public Object access(ProceedingJoinPoint jp, Method method, DoHystrix doHystrix, Object[] args) {
        this.jp = jp;
        this.method = method;
        this.doHystrix = doHystrix;

        // 设置熔断超时时间
        Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GovernGroup"))
              .andCommandPropertiesDefaults(HystrixCommandProperties
                      .Setter()
                      .withCircuitBreakerRequestVolumeThreshold(1)                // 至少有1个请求，熔断器才会开始进行错误率计算
                      .withCircuitBreakerSleepWindowInMilliseconds(50000000)      // 熔断器中断请求，n 秒后会进入一个半打开状态,放开部分请求去进行重试
                      .withCircuitBreakerErrorThresholdPercentage(0)              // 错误率达到 50% 就开启熔断保护
                      .withExecutionTimeoutEnabled(true)
                      .withExecutionTimeoutInMilliseconds(doHystrix.timeout()));

        return this.execute();
    }

    @Override
    protected Object run() throws Exception {
        try {
            return jp.proceed();
        } catch (Throwable throwable) {
            return null;
        }
    }

    @Override
    protected Object getFallback() {
        try {
            FallbackHandler<?> fallbackHandler = doHystrix.fallbackHandler().newInstance();
            return fallbackHandler.handler(jp);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}
